package org.myh.excel.support;


import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.myh.excel.ExcelDataCallback;
import org.myh.excel.concurrent.AbstractDivisibleExcelBuffer;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * ClassName: ConcurrentDBConnectByExcel
 * Description: 并发数据库连接,CRE是Concurrent Read Excel
 * date: 2024/3/26 11:03
 *
 * @author Yu Han
 * @since JDK 1.8
 */
public class CRECallBackDataWithDBConnect {

    /**
     * 使用jdbc模式
     */
    public final static int JDBC_TYPE = 0;

    /**
     * 使用mybatis模式
     */
    public final static int MYBATIS_TYPE = 1;

    /**
     * 默认类型
     */
    private int type = JDBC_TYPE;

    /**
     * 若是jdbc模式需传入属性对象
     */
    private final Properties driverProperties;

    /**
     * 数据源
     */
    private final java.sql.Driver driver;

    /**
     * 线程池
     */
    private final ThreadPoolExecutor threadPoolExecutor;

    private final ReentrantLock lock = new ReentrantLock();

    /**
     * 具体操作excel的类
     */
    private AbstractDivisibleExcelBuffer divisibleExcelBuffer;

    /**
     * 打开前置处理
     */
    private boolean openPreHandle = false;

    /**
     * 打开后置处理
     */
    private boolean openPostHandle = false;

    /**
     * 非jdbc构造
     *
     * @param threadSize
     * @param type
     */
    public CRECallBackDataWithDBConnect(int threadSize, int type, boolean openPreHandle,boolean openPostHandle) {
        this(threadSize, type, null, null, openPreHandle, openPostHandle);
    }

    /**
     * 使用jdbc的方式,不传入驱动和属性文件则由用户默认实现
     *
     * @param threadSize
     * @param driver
     * @param properties
     */
    public CRECallBackDataWithDBConnect(int threadSize, int type, java.sql.Driver driver, Properties properties,boolean openPreHandle,boolean openPostHandle) {
        if (type >= JDBC_TYPE) {
            this.type = type;
            if (type == JDBC_TYPE) {
                if (driver == null || properties == null || properties.size() <= 0) {
                    throw new RuntimeException("缺少数据源");
                }
            }
        }

        threadPoolExecutor = new ThreadPoolExecutor(threadSize, threadSize, 5,
                TimeUnit.SECONDS, new LinkedBlockingDeque<>(100000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        this.driver = driver;
        this.driverProperties = properties;
        this.openPreHandle = openPreHandle;
        this.openPostHandle = openPostHandle;
    }

    public void setDivisibleExcelBuffer(AbstractDivisibleExcelBuffer divisibleExcelBuffer) {
        this.divisibleExcelBuffer = divisibleExcelBuffer;
    }

    /**
     * 切换表格
     *
     * @param sheetIndex
     */
    public void cutSheet(int sheetIndex) {
        this.divisibleExcelBuffer.refresh(sheetIndex);
    }

    Log log = LogFactory.getLog(CRECallBackDataWithDBConnect.class);
    /**
     * 执行数据读取 并回调
     *
     * @param callback
     * @throws Exception
     */
    public void exec(ExcelDataCallback callback) throws Exception {

        if (divisibleExcelBuffer == null) {
            throw new RuntimeException("缺少excelBuffer");
        }

        //开启一个分线程去执行读取excel操作
        threadPoolExecutor.execute(() -> {
            try {
                //前置数据处理器
                log.debug("执行读取excel" + Thread.currentThread().getName());
                if (openPreHandle) {
                    divisibleExcelBuffer.setCallBack(new AbstractDivisibleExcelBuffer.ExcelDataCallBack() {
                        @Override
                        public void sendRowData(int index, Object rowData,int total ,boolean hasNext) {
                            callback.preExcelDataHandle(index, rowData,total,hasNext);
                        }
                    });
                }
                divisibleExcelBuffer.readExcel();
                log.debug("读取方法结束");
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        //基于多线程的模式

        //线程池操作数据 并回调给使用者
        while (divisibleExcelBuffer.hasNext()) {
            //若是有数据
            HashMap<Integer, List<Object>> next = divisibleExcelBuffer.next();
            if (next == null) {
                Thread.sleep(5);
                continue;
            }

            //这里会有并发问题
            //抢占锁成功
            threadPoolExecutor.execute(() -> {
                Enhancer enhancer = new Enhancer();

                enhancer.setSuperclass(callback.getClass());

                enhancer.setCallback(new MethodInterceptor() {
                    @Override
                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        methodProxy.invokeSuper(o, objects);
                        return o;
                    }
                });

                ExcelDataCallback excelDataCallback = (ExcelDataCallback) enhancer.create();

                //开启jdbc事务执行
                //用户自己的处理逻辑
                //创建了新的数据库连接
                if (type == JDBC_TYPE) {
                    Connection connect = null;
                    try {
                        connect = driver.connect(driverProperties.getProperty("url"), driverProperties);
                    } catch (SQLException e) {
                        e.printStackTrace();
                        //连接失败 记录此次记录
                    }
                    excelDataCallback.getConnectAndExcelData(connect, next);
                }

                if (type == MYBATIS_TYPE) {
                    excelDataCallback.getConnectAndExcelData(next);
                }
            });
        }

        if(openPostHandle){
            callback.postExcelDataHandle(threadPoolExecutor);
        }
        threadPoolExecutor.shutdown();
        Runtime.getRuntime().addShutdownHook(new Thread(()->{
            if(!threadPoolExecutor.isShutdown()){
                threadPoolExecutor.shutdown();
            }
        }));
    }
}
